#include <stdio.h>
#include <string.h>

#include "command.h"
#include "common.h"
#include "utils.h"

char cmd_buf[CMD_BUF_SIZE + 1];
char args_buf[CMD_BUF_SIZE + 1];

static tmodule *cur_module = NULL;
static int arg_ind;
static char *av_poi;

static int module_init;

int get_cmd(char *buf)
{
    int len;

    if((fgets(buf,CMD_BUF_SIZE,stdin)) == NULL)
    {
        perror("no message.\n");
        return 0;
    }  
    len = strlen(buf);
    buf[len -1] = '\0';
    //printf("%s,%d\n",buf,len);

    return len;
}

void reset_argind(void)
{
    arg_ind = 0;
    av_poi = NULL;
}

char *get_argvalue(void)
{
    return av_poi;
}

int parse_args(int argc,char *argv[],const char *osi)
{
    char *parg;
    int opt;
    int pre_opt;

    if((arg_ind >= argc) || (NULL == argv)){
        return -1;
    }

    parg = argv[arg_ind];
    arg_ind++;
    av_poi = NULL;

    /*search option flag*/
    while(('\0' != *parg) && ('-' != *parg)){
        parg++;
    }
    /*skip option flag*/
    while(('\0' != *parg) && ('-' != *parg)){
        parg++;
    }
    if(strlen(parg) < 1){
        return -1;
    }

    opt = 0;
    /*search option end flag*/
    while('\0' != *parg){
        if('=' == *parg){
            parg++;
            break;
         }
         opt += *parg;
         parg++;
     }
     /* match option string index*/
     pre_opt = 0;
     while('\0' != *osi){
        if(':' == *osi){
            if(opt == pre_opt){
                break;
            }
            pre_opt = 0;
            osi++;
            continue;
         }
         pre_opt += *osi;
         osi++;
     }

     if(opt == pre_opt){
        if('\0' != *parg){
            av_poi = parg;
        }
        return opt;
      }

      return -1;
}

tcmd *find_cmd(const char *name)
{
    extern u32 _cmd_base;
    extern u32 _cmd_end;

    tcmd *cmdtp;
    tcmd *cmdtp_temp;
    tcmd *table_start;
    tcmd *table_end;

    int len;
    int n_found;

    if((NULL == name) || ('\0' == *name)){
        return NULL;
    }

    len = strlen(name);
    table_start = (tcmd *)(&_cmd_base);
    table_end = (tcmd *)(&_cmd_end);

    printf("find_cmd,%s,len %d\n",name,len);
    cmdtp_temp = table_start;
    for(cmdtp = table_start;cmdtp < (tcmd *)table_end;cmdtp++){
        //printf("cmdtp->name:%s\n",cmdtp->name);
        if(strncmp(name,cmdtp->name,len) == 0){
            if(len == strlen(cmdtp->name)){
                return cmdtp;
            }
            cmdtp_temp = cmdtp;
            n_found++;
        }
    }
    if(1 == n_found){
        return cmdtp_temp;
    }
    
    return NULL;    
}

tmodule *find_module(const char *name)
{
    extern u32 _module_base;
    extern u32 _module_end;

    tmodule *cmdtp;
    tmodule *cmdtp_temp;
    tmodule *table_start;
    tmodule *table_end;
    int len;
    int n_found = 0;
    
    if((NULL == name) || ('\0' == *name)){
        return cur_module;
    }
    len = strlen(name);

    printf("find_module,name %s,len %d\n",name,len);
    if(NULL != cur_module){
        if(strncmp(name,cur_module->name,len) == 0){
            if(len == strlen(cur_module->name)){
                return cur_module;
            }
        }
    }

    table_start = (tmodule *)(&_module_base);
    table_end = (tmodule *)(&_module_end);

    cmdtp_temp = table_start;
    for(cmdtp = table_start;cmdtp < table_end;cmdtp++){
        //printf("cmdtp->name %s\n",cmdtp->name);
        if(strncmp(name,cmdtp->name,len) == 0){
            if(len == strlen(cmdtp->name)){
                cur_module = cmdtp;
                return cmdtp;
            }
            cmdtp_temp = cmdtp;
            n_found++;
        }
    }
    if(1 == n_found){
        cur_module = cmdtp_temp;
        return cmdtp_temp;
    }
    return NULL;
}

titem *find_item(tmodule *module,const char *item_name)
{
    titem *item;
    titem *item_temp;

    int len;
    int n_found = 0;

    if((NULL == module) || (NULL == item_name)){
        return NULL;
    }
    item = module->item_poi;

    printf("find_item,item_name %s\n",item_name);
    if(NULL == item){
        return NULL;
    }
    len = strlen(item_name);
    item_temp = item;

    while(NULL != item->name){
        if(strncmp(item_name,item->name,len) == 0){
            if(len == strlen(item->name)){
                return item;
            }
            item_temp = item;
            n_found++;
        }
        item++;
    }
    if(1 == n_found){
        return item_temp;
    }
    return NULL;
}

int pre_proc_cmd(const char *cmdline,char *cmdbuf)
{   
    int cmd_len;
    const char *pstr;
    char *pcmd;
    char *ptmp;
    char cmd_name[CMD_NAME_SIZE + 1];

    pstr = cmdline;
    pcmd = cmdbuf;

    printf("pre_proc_cmd,cmdline %s\n",cmdline);
    ptmp = strchr(pstr,'-');

    if(NULL != ptmp){
        ptmp++;
        /*check if help*/
        if((*ptmp == 'h')
            ||(strncmp(ptmp,"help",4) == 0)){
            ptmp = cmd_name;
            if(strncmp(pstr,MODULE_FLAG,sizeof(MODULE_FLAG)-1) == 0){/*module*/
                pstr +=sizeof(MODULE_FLAG) - 1;
                while(('\0' != *pstr) && (SPACE != *pstr)){
                    *ptmp++ = *pstr++;
                }
                *ptmp = '\0';
                if(NULL == find_module(cmd_name)){
                    printf("No module %s to test\n",cmd_name);
                    cmd_len = 0;
                }else{
                    cmd_len = sprintf(pcmd,"help -m=%s -a",cmd_name);
                }
            }else {/*item*/
                if(NULL == cur_module){
                    printf("No selected moudle\n");
                    cmd_len = 0;
                }else{
                    while(('\0' != *pstr) && (SPACE != *pstr)){
                        *ptmp++ = *pstr++;
                    }
                    *ptmp = '\0';
                    cmd_len = sprintf(pcmd,"help -m=%s -i=%s",cur_module->name,cmd_name);
                }
            }
            
            printf("pcmd=%s,cmd_len=%d\n",pcmd,cmd_len);
            return cmd_len;
        }
    }

    cmd_len = strlen(pstr);
    strcpy(pcmd,pstr);
    return cmd_len;
}

void cmd_set_timeout(u32 us)
{
    printf("cmd_set_timeout\n");
}

void cmd_clear_timeout(void)
{
    printf("cmd_clear_timeout\n");
}

static cmd_res exec_cmd(char *name,int argc,char *argv[])
{
    tcmd *cmd;

    if((NULL == name) || ('\0')){
        return ERR;
    }

    cmd = find_cmd(name);

    if(NULL == cmd){
        return MISMATCH;
    }
    if(cmd->func == NULL){
        printf("Command %s No command execute function\n",cmd->name);
        return ERR;
    }

    if(OK != cmd->func(argc,argv)){
        printf("Command %s execute failed\n",cmd->name);
        return ERR;
    }
    return OK;
}

static cmd_res exec_test(const char *cmd_name,int argc,char *argv[])
{
    tmodule *module;
    titem *item = NULL;
    tfunc testfunc = NULL;
    terr_proc err_proc = NULL;
    bool is_module = FALSE;
    u32 time_out;

    printf("exec_test,cmd_name:%s\n",cmd_name);
    if(0 == strncmp(cmd_name,MODULE_FLAG,sizeof(MODULE_FLAG)-1)){
        is_module = TRUE;
        cmd_name += sizeof(MODULE_FLAG) - 1;
        /*Look up module in module table*/
        module = find_module(cmd_name);
        module_init = 0;
        if(NULL != module){
            testfunc = module->func;
            err_proc = module->err_proc;
            time_out = module->timeout;
        }else{
            printf("Unkown module '%s' - try 'help'\n",cmd_name);
        }
    }else{
        module = cur_module;
        if(NULL == cur_module){
            printf("No Module to test,- try 'help'\n");
            return ERR;
        }
        item =  find_item(module,cmd_name);
        if(NULL != item){
            testfunc = item->func;
            err_proc = item->err_proc;
            time_out = item->timeout;
        }else{
            printf("Module '%s' has no item '%s',- try 'help'\n",module->name,cmd_name);
            return ERR;
        }        
    }

    if(NULL != testfunc){
        cmd_res result;
        /*if the test is item,but moudle is not initialized,we can't execute item test*/
        if((FALSE == is_module) && (0 == module_init)){
            printf("Module '%s' doesn't initialize",module->name);
            return ERR;
        }

        if(0xFFFFFFFF != time_out){
            cmd_set_timeout(time_out);
        }
        result = testfunc(argc,argv);

        if(0xFFFFFFFF != time_out){
            cmd_clear_timeout();
        }
        if(OK != result){
            if(TRUE == is_module){
                printf("Module '%s' execute err,- try 'help'\n",module->name);
            }else{
                printf("Test item '%s' execute err,- try 'help'\n",item->name);
            }
        }else{
            /*if execute result is OK,and the test is module,we nee indicate that
            module has been initizalized*/
            if(TRUE == is_module){
                module_init = 1;
            }
        }

        if(NULL != err_proc){
            err_proc(result);
        }
    }else{
        if(TRUE == is_module){
            module_init = 1;//indicate current module has been initialized
        }
    }

    printf("exec_test,retrun OK\n");
    return OK;
}

int exec_cmdline(const char *cmdline)
{
    char *pcmd_buf;
    char *argv[CMD_ARGC_MAX + 1];
    char *cmd_name;
    int argc;
    cmd_res status = OK;
    char cmdbuf[CMD_BUF_SIZE + 1];

    if(!cmdline || !*cmdline){
        return ERR;
    }

    if(strlen(cmdline) >= CMD_BUF_SIZE){
        perror("Command too long.\n");
        return ERR;
    }

    if(pre_proc_cmd(cmdline,cmdbuf) < 1){
        return ERR;
    }

    if((pcmd_buf = skip_space(cmdbuf)) == NULL){
        printf("invalid command string.\n");
    }

    argc = split_str2argv(pcmd_buf,argv,SPACE);

    if(argc < 1){
        return ERR;
    }

    cmd_name = argv[0];/*execute cmd*/
    status = exec_cmd(cmd_name,argc-1,&argv[1]);

    if(MISMATCH == status){/*module and item test*/
        status = exec_test(cmd_name,argc-1,&argv[1]);
    }

    return status;
}

int make_cmd(char *cmd,const char *name,const char *args,bool module)
{
    if(TRUE == module){
        return sprintf(cmd,"%s%s %s",MODULE_FLAG,name,args);
    }else{
        return sprintf(cmd,"%s %s",name,args);
    }
}